﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;

namespace KP_CommonLibrary.KP_CommonControls.NumericInput
{
    public class NumericFormatter
{
    // Fields
    private readonly string decimalSeparator;
    private readonly List<string> formatMask = new List<string>();
    private readonly string groupSeparator;
    private readonly int[] groupSizes;
    public readonly bool Is100Multiplied;
    private readonly int maxDigitsAfterDecimalSeparator;
    private readonly int maxDigitsBeforeDecimalSeparator;
    private readonly int minDigitsAfterDecimalSeparator;
    private readonly int minDigitsBeforeDecimalSeparator;

    // Methods
    public NumericFormatter(string formatString, CultureInfo formattingCulture)
    {
        CharEnumerator enumerator = formatString.GetEnumerator();
        bool flag = false;
        bool flag2 = false;
        NumericMaskSubType number = NumericMaskSubType.Number;
        while (enumerator.MoveNext())
        {
            char ch;
            switch (enumerator.Current)
            {
                case '#':
                    if (!flag)
                    {
                        goto Label_00CE;
                    }
                    this.maxDigitsAfterDecimalSeparator++;
                    goto Label_00DC;

                case '$':
                {
                    number = NumericMaskSubType.Currency;
                    this.formatMask.Add(formattingCulture.NumberFormat.CurrencySymbol);
                    continue;
                }
                case '%':
                {
                    CharEnumerator enumerator2 = (CharEnumerator) enumerator.Clone();
                    if (!enumerator2.MoveNext() || (enumerator2.Current != '%'))
                    {
                        goto Label_0166;
                    }
                    enumerator.MoveNext();
                    number = NumericMaskSubType.PercentPercent;
                    goto Label_0168;
                }
                case ',':
                {
                    if (flag)
                    {
                        goto Label_0183;
                    }
                    flag2 = true;
                    continue;
                }
                case '.':
                {
                    if (flag)
                    {
                        goto Label_0183;
                    }
                    flag = true;
                    this.formatMask.Add(null);
                    continue;
                }
                case '0':
                    if (!flag)
                    {
                        break;
                    }
                    this.maxDigitsAfterDecimalSeparator++;
                    this.minDigitsAfterDecimalSeparator++;
                    goto Label_00AA;

                case '\\':
                    if (!enumerator.MoveNext())
                    {
                        continue;
                    }
                    goto Label_0183;

                default:
                    goto Label_0183;
            }
            this.maxDigitsBeforeDecimalSeparator++;
            this.minDigitsBeforeDecimalSeparator++;
        Label_00AA:
            this.formatMask.Add(null);
            continue;
        Label_00CE:
            this.maxDigitsBeforeDecimalSeparator++;
        Label_00DC:
            this.formatMask.Add(null);
            continue;
        Label_0166:
            number = NumericMaskSubType.Percent;
        Label_0168:
            this.formatMask.Add(formattingCulture.NumberFormat.PercentSymbol);
            continue;
        Label_0183:
            ch = enumerator.Current;
            if ((this.formatMask.Count > 0) && (this.formatMask[this.formatMask.Count - 1] != null))
            {
                this.formatMask[this.formatMask.Count - 1] = this.formatMask[this.formatMask.Count - 1] + ch;
            }
            else
            {
                this.formatMask.Add(ch.ToString(formattingCulture));
            }
        }
        switch (number)
        {
            case NumericMaskSubType.Currency:
                this.decimalSeparator = formattingCulture.NumberFormat.CurrencyDecimalSeparator;
                this.groupSizes = formattingCulture.NumberFormat.CurrencyGroupSizes;
                this.groupSeparator = formattingCulture.NumberFormat.CurrencyGroupSeparator;
                break;

            case NumericMaskSubType.Percent:
            case NumericMaskSubType.PercentPercent:
                this.decimalSeparator = formattingCulture.NumberFormat.PercentDecimalSeparator;
                this.groupSizes = formattingCulture.NumberFormat.PercentGroupSizes;
                this.groupSeparator = formattingCulture.NumberFormat.PercentGroupSeparator;
                break;

            default:
                this.decimalSeparator = formattingCulture.NumberFormat.NumberDecimalSeparator;
                this.groupSizes = formattingCulture.NumberFormat.NumberGroupSizes;
                this.groupSeparator = formattingCulture.NumberFormat.NumberGroupSeparator;
                break;
        }
        if (!flag2)
        {
            this.groupSizes = null;
        }
        this.Is100Multiplied = number == NumericMaskSubType.Percent;
    }

    private static string CreateCurrencyFormat(int precision, CultureInfo culture)
    {
        string str2;
        string str3;
        if (precision < 0)
        {
            precision = culture.NumberFormat.CurrencyDecimalDigits;
        }
        int num = 0x1d - precision;
        if (num < 1)
        {
            num = 1;
        }
        string str = new string('#', num - 1) + ",0";
        if (precision > 0)
        {
            str = str + '.' + new string('0', precision);
        }
        switch (culture.NumberFormat.CurrencyPositivePattern)
        {
            case 1:
                str2 = "{0}$";
                break;

            case 2:
                str2 = "$ {0}";
                break;

            case 3:
                str2 = "{0} $";
                break;

            default:
                str2 = "${0}";
                break;
        }
        switch (culture.NumberFormat.CurrencyNegativePattern)
        {
            case 1:
                str3 = "{1}${0}";
                break;

            case 2:
                str3 = "${1}{0}";
                break;

            case 3:
                str3 = "${0}{1}";
                break;

            case 4:
                str3 = "({0}$)";
                break;

            case 5:
                str3 = "{1}{0}$";
                break;

            case 6:
                str3 = "{0}{1}$";
                break;

            case 7:
                str3 = "{0}${1}";
                break;

            case 8:
                str3 = "{1}{0} $";
                break;

            case 9:
                str3 = "{1}$ {0}";
                break;

            case 10:
                str3 = "{0} ${1}";
                break;

            case 11:
                str3 = "$ {0}{1}";
                break;

            case 12:
                str3 = "$ {1}{0}";
                break;

            case 13:
                str3 = "{0}{1} $";
                break;

            case 14:
                str3 = "($ {0})";
                break;

            case 15:
                str3 = "({0} $)";
                break;

            default:
                str3 = "(${0})";
                break;
        }
        return string.Format(CultureInfo.InvariantCulture, str2 + ';' + str3, new object[] { str, GetNegativeSymbolPattern(culture) });
    }

    private static string CreateDecimalFormat(int precision, CultureInfo culture)
    {
        string str;
        if (precision <= 0)
        {
            str = new string('#', 0x1c) + "0";
        }
        else
        {
            str = new string('0', precision);
        }
        return string.Format(CultureInfo.InvariantCulture, "{0};{1}{0}", new object[] { str, GetNegativeSymbolPattern(culture) });
    }

    private static string CreateFixedPointFormat(int precision, CultureInfo culture)
    {
        if (precision < 0)
        {
            precision = culture.NumberFormat.NumberDecimalDigits;
        }
        int num = 0x1d - precision;
        if (num < 1)
        {
            num = 1;
        }
        string str = new string('#', num - 1) + "0";
        if (precision > 0)
        {
            str = str + '.' + new string('0', precision);
        }
        return string.Format(CultureInfo.InvariantCulture, "{0};{1}{0}", new object[] { str, GetNegativeSymbolPattern(culture) });
    }

    private static string CreateFullNumberFormatFromPositiveFormat(string numberFormat, CultureInfo culture)
    {
        string str;
        switch (culture.NumberFormat.NumberNegativePattern)
        {
            case 1:
                str = "{1}{0}";
                break;

            case 2:
                str = "{1} {0}";
                break;

            case 3:
                str = "{0}{1}";
                break;

            case 4:
                str = "{0} {1}";
                break;

            default:
                str = "({0})";
                break;
        }
        return string.Format(CultureInfo.InvariantCulture, "{0};" + str, new object[] { numberFormat, GetNegativeSymbolPattern(culture) });
    }

    private static string CreateNumberFormat(int precision, CultureInfo culture)
    {
        if (precision < 0)
        {
            precision = culture.NumberFormat.NumberDecimalDigits;
        }
        int num = 0x1d - precision;
        if (num < 1)
        {
            num = 1;
        }
        string numberFormat = new string('#', num - 1) + ",0";
        if (precision > 0)
        {
            numberFormat = numberFormat + '.' + new string('0', precision);
        }
        return CreateFullNumberFormatFromPositiveFormat(numberFormat, culture);
    }

    private static string CreatePercentFormat(int precision, CultureInfo culture, string percentSymbol)
    {
        string str2;
        string str3;
        if (precision < 0)
        {
            precision = culture.NumberFormat.PercentDecimalDigits;
        }
        int num = 0x1d - precision;
        if (num < 1)
        {
            num = 1;
        }
        string str = new string('#', num - 1) + ",0";
        if (precision > 0)
        {
            str = str + '.' + new string('0', precision);
        }
        switch (culture.NumberFormat.PercentPositivePattern)
        {
            case 1:
                str2 = "{0}{1}";
                break;

            case 2:
                str2 = "{1}{0}";
                break;

            default:
                str2 = "{0} {1}";
                break;
        }
        switch (culture.NumberFormat.PercentNegativePattern)
        {
            case 1:
                str3 = "{2}{0}{1}";
                break;

            case 2:
                str3 = "{2}{1}{0}";
                break;

            default:
                str3 = "{2}{0} {1}";
                break;
        }
        return string.Format(CultureInfo.InvariantCulture, str2 + ';' + str3, new object[] { str, percentSymbol, GetNegativeSymbolPattern(culture) });
    }

    public static string Expand(string formatString, CultureInfo culture)
    {
        int num2;
        if ((formatString == null) || (formatString.Length == 0))
        {
            formatString = new string('#', 0x1d) + ",0." + new string('#', 30);
        }
        if (Regex.IsMatch(formatString, "^[cCdDgGfFnNpP][0-9]{0,2}$"))
        {
            int precision = (formatString.Length > 1) ? Convert.ToInt32(formatString.Substring(1)) : -1;
            switch (formatString[0])
            {
                case 'C':
                case 'c':
                    formatString = CreateCurrencyFormat(precision, culture);
                    goto Label_0137;

                case 'D':
                case 'd':
                    formatString = CreateDecimalFormat(precision, culture);
                    goto Label_0137;

                case 'F':
                case 'G':
                case 'f':
                case 'g':
                    formatString = CreateFixedPointFormat(precision, culture);
                    goto Label_0137;

                case 'N':
                case 'n':
                    formatString = CreateNumberFormat(precision, culture);
                    goto Label_0137;

                case 'P':
                    formatString = CreatePercentFormat(precision, culture, "%%");
                    goto Label_0137;

                case 'p':
                    formatString = CreatePercentFormat(precision, culture, "%");
                    goto Label_0137;
            }
            throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Internal error: non-covered case '{0}'", new object[] { formatString }));
        }
    Label_0137:
        num2 = formatString.Replace(@"\\", "//").Replace(@"\;", "/:").IndexOf(';');
        if (num2 < 0)
        {
            formatString = CreateFullNumberFormatFromPositiveFormat(formatString, culture);
            return formatString;
        }
        if (num2 == (formatString.Length - 1))
        {
            formatString = formatString.Substring(0, formatString.Length - 1);
        }
        return formatString;
    }

    public string Format(string source)
    {
        return this.Format(source, -1);
    }

    private string Format(string source, int sourcePositionForTerminate)
    {
        int index = source.IndexOf('.');
        if (index < 0)
        {
            index = source.Length;
        }
        string str = string.Empty;
        int num2 = 0;
        int maxDigitsBeforeDecimalSeparator = this.maxDigitsBeforeDecimalSeparator;
        foreach (string str2 in this.formatMask)
        {
            if (str2 != null)
            {
                str = str + str2;
            }
            else
            {
                if (num2 == sourcePositionForTerminate)
                {
                    return str;
                }
                if (maxDigitsBeforeDecimalSeparator == 0)
                {
                    num2++;
                    str = str + this.decimalSeparator;
                }
                else
                {
                    if (maxDigitsBeforeDecimalSeparator == this.maxDigitsBeforeDecimalSeparator)
                    {
                        while ((index - num2) > this.maxDigitsBeforeDecimalSeparator)
                        {
                            str = str + source[num2];
                            str = str + this.GetSeparator(index - num2);
                            num2++;
                            if (num2 == sourcePositionForTerminate)
                            {
                                return str;
                            }
                        }
                    }
                    if ((index >= maxDigitsBeforeDecimalSeparator) && (num2 < source.Length))
                    {
                        str = str + source[num2];
                        str = str + this.GetSeparator(index - num2);
                        num2++;
                        if (num2 == sourcePositionForTerminate)
                        {
                            return str;
                        }
                    }
                }
                maxDigitsBeforeDecimalSeparator--;
            }
        }
        return str;
    }

    private static string GetNegativeSymbolPattern(CultureInfo culture)
    {
        string str = string.Empty;
        foreach (char ch in culture.NumberFormat.NegativeSign)
        {
            str = str + '\\' + ch;
        }
        return str;
    }

    public int GetPositionFormatted(string source, int sourcePosition)
    {
        return this.Format(source, sourcePosition).Length;
    }

    public int GetPositionSource(string source, int formattedPosition)
    {
        for (int i = 0; i < source.Length; i++)
        {
            int positionFormatted = this.GetPositionFormatted(source, i);
            if (formattedPosition <= positionFormatted)
            {
                return i;
            }
        }
        return source.Length;
    }

    private string GetSeparator(int positionFromDecimalSeparator)
    {
        if (((positionFromDecimalSeparator > 1) && (this.groupSizes != null)) && (this.groupSizes.Length != 0))
        {
            int num = 1;
            for (int i = 0; i < (this.groupSizes.Length - 1); i++)
            {
                int num3 = num + this.groupSizes[i];
                if (num3 == positionFromDecimalSeparator)
                {
                    return this.groupSeparator;
                }
                if (num3 > positionFromDecimalSeparator)
                {
                    return string.Empty;
                }
                num = num3;
            }
            int num4 = this.groupSizes[this.groupSizes.Length - 1];
            if ((num4 != 0) && (((positionFromDecimalSeparator - num) % num4) == 0))
            {
                return this.groupSeparator;
            }
        }
        return string.Empty;
    }

    // Properties
    public int MaxDigitsAfterDecimalSeparator
    {
        get
        {
            return this.maxDigitsAfterDecimalSeparator;
        }
    }

    public int MaxDigitsBeforeDecimalSeparator
    {
        get
        {
            return this.maxDigitsBeforeDecimalSeparator;
        }
    }

    public int MinDigitsAfterDecimalSeparator
    {
        get
        {
            return this.minDigitsAfterDecimalSeparator;
        }
    }

    public int MinDigitsBeforeDecimalSeparator
    {
        get
        {
            return this.minDigitsBeforeDecimalSeparator;
        }
    }

    // Nested Types
    private enum NumericMaskSubType
    {
        Number,
        Currency,
        Percent,
        PercentPercent
    }
}  
}
